package com.gitee.huanminabc.db_freedom.wrapper;

import com.gitee.huanminabc.db_freedom.base.PreTable;
import com.gitee.huanminabc.db_freedom.base.SqlFun;
import com.gitee.huanminabc.db_freedom.config.MybatisFreedomConfig;
import com.gitee.huanminabc.db_freedom.proxy.ProxyCglibFactory;
import com.gitee.huanminabc.db_freedom.proxy.ProxyCglibInstance;
import com.gitee.huanminabc.db_freedom.segments.SqlStructureImplSegments;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.Arrays;

@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class LambdaSQL<T> extends SqlStructureImplSegments<T,LambdaSQL<T>>  {

    private LambdaSQL(Class<?> entityClass, SQL sql) {
        super(entityClass, sql);
    }

    //关联主表, 这个实体用于接收查询结果的,无须和数据库表完全对应
    public static <T> LambdaSQL<T> create(Class<T> entity){
        LambdaSQL<T> tLambdaSql = new LambdaSQL<>(entity, new SQL());
        ProxyCglibFactory proxyCglibFactory = new ProxyCglibFactory(MybatisFreedomConfig.actionProxy, tLambdaSql);
        Object object = ProxyCglibInstance.getObject(tLambdaSql, proxyCglibFactory);
        return  (LambdaSQL<T>) object;
    }
    public static <T> LambdaSQL<T> create(Class<T> entity,String alias){
        LambdaSQL<T> tLambdaSql = new LambdaSQL<>(entity, new SQL());
        tLambdaSql.getTableInfo().setAlias(alias); //因为是最开始的表,所以别名不可能会有重复,这里就不用检查
        ProxyCglibFactory proxyCglibFactory = new ProxyCglibFactory(MybatisFreedomConfig.actionProxy, tLambdaSql);
        Object object = ProxyCglibInstance.getObject(tLambdaSql, proxyCglibFactory);
        return  (LambdaSQL<T>) object;
    }

    /**
     * 不使用lambda表达式 ,自己写sql  站位符为?
     * 兼容参数拦截和sql注入处理
     * 此方法使用后不能再使用lambda后续的方法了
     * @param entity  主表实体类  ,用于接收查询结果
     * @param sql sql语句
     * @param params 参数
     * @return LambdaSQL
     * @param <T>
     */
    public static <T> LambdaSQL<T> create(Class<T> entity,String sql, Object... params){
        SQL sql1 = new SQL();
        sql1.setSql(new StringBuilder(sql));
        sql1.setParams(Arrays.asList(params));
        LambdaSQL<T> tLambdaSql = new LambdaSQL<>(entity, sql1);
        ProxyCglibFactory proxyCglibFactory = new ProxyCglibFactory(MybatisFreedomConfig.actionProxy, tLambdaSql);
        Object object = ProxyCglibInstance.getObject(tLambdaSql, proxyCglibFactory);
        return  (LambdaSQL<T>) object;
    }



    //===========================下面是增强方法===========================

    //内部使用
    private static <T> LambdaSQL<T> create_(Class<?> entity){
        return  create((Class<T>) entity);
    }



    //预注册表 ,用于后面列你需要自定义sql的时候,就必须提前将需要的表注册进来,操作的时候使用别名,不然生成的表别名啥的都是内置的

    public LambdaSQL<T> PRE_TABLE(PreTable... preTables){
        for (PreTable preTable : preTables) {
            PRE_TABLE(preTable.getEntityClass(),preTable.getAlias());
        }
        return typedThis;
    }
    private LambdaSQL<T> PRE_TABLE(Class<?> entityClass,String alias){
        //判断之前是否已经加入过
        TableInfo tableInfo = getEntityClassList().get(entityClass.getName());
        if(tableInfo!=null){
            throw new RuntimeException("表已经存在");
        }
        TableInfo tableInfo1 = this.createTableInfo(entityClass);
        tableInfo1.setAlias(alias);
        return typedThis;
    }


    //通过其他的LambdaSQL生成子查询
    public <Y> LambdaSQL<T> SON(LambdaSQL<Y> lambdaSQL){
        _SON_(lambdaSQL, null);
        return typedThis;
    }
    //通过其他的LambdaSQL生成子查询,只是说简化了创建lambdaSQL的过程
    public <Y> LambdaSQL<T> SON(Class<Y> entityClass, SqlFun<LambdaSQL<Y>,LambdaSQL<Y>> sqlFunctions) {
        LambdaSQL<Y> objectLambdaSQL = LambdaSQL.create_(entityClass);
        //拿到父的别名索引,让子的别名索引从父的别名索引开始
        objectLambdaSQL.setAliasIndex(this.getAliasIndex());
        _SON_(objectLambdaSQL,sqlFunctions);
        return typedThis;
    }
    //通过主表生成的子查询表
    public LambdaSQL<T> SON(SqlFun<LambdaSQL<T>,LambdaSQL<T>> sqlFunctions) {
        Class<?> entityClass = this.getTableInfo().getEntityClass();
        LambdaSQL<T> objectLambdaSQL = LambdaSQL.create_(entityClass);
        _SON_(objectLambdaSQL,sqlFunctions);
        return typedThis;
    }
    private <Y> void _SON_(LambdaSQL<Y> objectLambdaSQL,  SqlFun<LambdaSQL<Y>,LambdaSQL<Y>> sqlFunctions) {
        //拿到父的别名索引,让子的别名索引从父的别名索引开始
        objectLambdaSQL.setAliasIndex(this.getAliasIndex());
        String newAlias = objectLambdaSQL.getNewAlias(); //子的别名使用新的别名作为主表的别名
        objectLambdaSQL.getTableInfo().setAlias(newAlias);
        if(sqlFunctions!=null){
            LambdaSQL<Y> apply = sqlFunctions.apply(objectLambdaSQL);
            SQL().append(LEFT_BRACKET()).append(apply.SQL().toString()).append(RIGHT_BRACKET());
        }else{
            SQL().append(LEFT_BRACKET()).append(objectLambdaSQL.SQL().toString()).append(RIGHT_BRACKET());
        }
        //子别名索引赋值给父,这样就能保证别名永远是递增的
        this.setAliasIndex(objectLambdaSQL.getAliasIndex());
        //子表的操作记录合并
        this.getOperationRecord().addAll(objectLambdaSQL.getOperationRecord());
    }

    //通过其他的LambdaSQL生成子查询表
    public <Y> LambdaSQL<T> TABLE(LambdaSQL<Y> objectLambdaSQL){
        _TABLE_(objectLambdaSQL,null);
        return typedThis;
    }
    //通过其他的LambdaSQL生成子查询表, 多个虚拟表
    public LambdaSQL<T> TABLES(LambdaSQL... objectLambdaSQL){
        //判断是否为空
        if(objectLambdaSQL==null||objectLambdaSQL.length==0){
            throw new RuntimeException("TABLES方法参数不能为空");
        }
        for (LambdaSQL yLambdaSQL : objectLambdaSQL) {
            _TABLE_(yLambdaSQL,null);
            SQL().append(",");
        }
        //删除最后一个逗号
        SQL().deleteCharAt(SQL().length()-1);
        return typedThis;
    }

    //通过主表生成的子查询表
    public  <Y> LambdaSQL<T> TABLE(SqlFun<LambdaSQL<T>,LambdaSQL<T>> sqlFunctions){
        Class<?> entityClass = this.getTableInfo().getEntityClass();
        LambdaSQL<T> objectLambdaSQL = LambdaSQL.create_(entityClass);
        _TABLE_(objectLambdaSQL,sqlFunctions);
        return typedThis;
    }

    //通过其他的LambdaSQL生成子查询表 , 只是说简化了创建lambdaSQL的过程
    public  <Y> LambdaSQL<T> TABLE(Class<Y> entityClass, SqlFun<LambdaSQL<Y>,LambdaSQL<Y>> sqlFunctions){
        LambdaSQL<Y> objectLambdaSQL = LambdaSQL.create_(entityClass);
        _TABLE_(objectLambdaSQL,sqlFunctions);
        return typedThis;
    }

    private   <Y>  void   _TABLE_( LambdaSQL<Y> objectLambdaSQL, SqlFun<LambdaSQL<Y>,LambdaSQL<Y>> sqlFunctions){
        //拿到父的别名索引,让子的别名索引从父的别名索引开始
        objectLambdaSQL.setAliasIndex(this.getAliasIndex());
        String newAlias = objectLambdaSQL.getNewAlias(); //子的别名使用新的别名作为主表的别名
        objectLambdaSQL.getTableInfo().setAlias(newAlias);
        if(sqlFunctions!=null){
            LambdaSQL<Y> apply = sqlFunctions.apply(objectLambdaSQL);
            SQL().append(LEFT_BRACKET()).append("\n").append(apply.SQL().toString()).append(RIGHT_BRACKET());
        }else{
            SQL().append(LEFT_BRACKET()).append("\n").append(objectLambdaSQL.SQL().toString()).append(RIGHT_BRACKET());
        }
        //子别名索引赋值给父,这样就能保证别名永远是递增的
        this.setAliasIndex(objectLambdaSQL.getAliasIndex());
        //新的别名 作为虚拟表的别名
        String newAlias1 = this.getNewAlias();
        SQL().append(" ").append(newAlias1);

        //将子查询的主表映射加入到父查询的映射中
        TableInfo tableInfo = objectLambdaSQL.getTableInfo();
        tableInfo.setAlias(newAlias1);
        //判断类似是否是父主表类
        if (tableInfo.getEntityClass().equals(this.getTableInfo().getEntityClass())) {
            //如果是父主表类,那么修改父主表的别名
            this.getTableInfo().setAlias(newAlias1);
        }else {
            super.addTable(tableInfo);
        }


        //子表的操作记录合并
        this.getOperationRecord().addAll(objectLambdaSQL.getOperationRecord());
    }




}
